///////////////////////////////////////////////////////////////////////////////
//    Copyright (c) 2021 CASTest Corporation Limited. All Rights Reserved    //
///////////////////////////////////////////////////////////////////////////////

#ifndef _THREADING_H
#define _THREADING_H

class ThreadPool {
    typedef function<void()> Task;
public:
    vector<thread> m_pool;     // 线程池
    queue<Task> m_tasks;    // 任务队列
    mutex m_lock;    // 同步锁
    condition_variable m_cv;   // 条件阻塞

    atomic<bool> m_isStoped;    // 是否关闭提交
    atomic<int> m_idleThreadNum;  //空闲线程数量
    atomic<int> m_thread_num;

public:
    future<bool> res[MAX_FUTURE_NUM];

    ThreadPool() : m_isStoped(false) {
        //size = size > MAX_THREAD_NUM ? MAX_THREAD_NUM : size;
        m_idleThreadNum = MAX_THREAD_NUM;
        m_thread_num = MAX_THREAD_NUM;

        for (int i = 0; i < MAX_THREAD_NUM; i++) {
            //初始化线程数量
            m_pool.emplace_back(&ThreadPool::scheduler, this);
        }
    }

    ~ThreadPool() {
        printf("Destroy.\n");
        Close();
        while (!m_tasks.empty()) {
            m_tasks.pop();
        }
        m_cv.notify_all();  // 唤醒所有线程执行
        for (thread& thread : m_pool) {
            if (thread.joinable()) {
                thread.join();  // 等待任务结束，前提是线程一定会执行完
            }
        }
        m_pool.clear();
    }

    // 打开线程池，重启任务提交
    void ReOpen() {
        if (m_isStoped) m_isStoped.store(false);
        m_cv.notify_all();
    }

    // 关闭线程池，停止提交新任务
    void Close() {
        if (!m_isStoped) m_isStoped.store(true);
    }

    // 判断线程池是否被关闭
    bool IsClosed() const {
        return m_isStoped.load();
    }

    // 获取当前任务队列中的任务数
    int GetTaskSize() {
        return m_tasks.size();
    }

    // 获取当前空闲线程数
    int IdleCount() {
        return m_idleThreadNum;
    }

    int GetThreadNum(){
        return m_thread_num;
    }

    void WaitTasks(int task_num) {
        int id = 0;
        int rest = task_num;
        while(id < task_num){
//            if(res[id].get()) {
//                if (rest <= 0) break;
//                rest --;
//            }
//            cout << "processing------------  " << double(task_num - rest) / double(task_num) * 100  << "%"<< endl;
//            id ++;
//            if (id == task_num) id = 0;
            while (!res[id].get());
            id ++;
        }
    }

    // 提交任务并执行
    // 调用方式为 std::future<returnType> var = threadpool.Submit(...)
    // var.get() 会等待任务执行完，并获取返回值
    // 其中 ... 可以直接用函数名+函数参数代替，例如 threadpool.Submit(f, 0, 1)
    // 但如果要调用类成员函数，则最好用如下方式
    // threadpool.Submit(std::bind(&Class::Func, &classInstance)) 或
    // threadpool.Submit(std::mem_fn(&Class::Func), &classInstance)
    template<class F, class... Args>
    auto Submit(F&& f, Args&&... args)->future<decltype(f(args...))> {
        if (m_isStoped.load()) {
            throw std::runtime_error("ThreadPool is closed, can not submit task.");
        }

        using RetType = decltype(f(args...));  // typename std::result_of<F(Args...)>::type, 函数 f 的返回值类型
        shared_ptr<packaged_task<RetType()>> task = make_shared<packaged_task<RetType()>>(
                bind(forward<F>(f), forward<Args>(args)...)
        );
        future<RetType> future = task->get_future();
        // 封装任务并添加到队列
        addTask([task](){
            (*task)();
        });

        return future;
    }

private:
    // 消费者
    Task getTask() {
        unique_lock<mutex> lock(m_lock); // unique_lock 相比 lock_guard 的好处是：可以随时 unlock() 和 lock()
        // wait 直到有 task
        while (m_tasks.empty() && !m_isStoped) {
            m_cv.wait(lock);
        }
        if (m_isStoped) {
            return Task();
        }
        assert(!m_tasks.empty());
        Task task = move(m_tasks.front()); // 取一个 task
        m_tasks.pop();
        m_cv.notify_one();
        return task;
    }

    // 生产者
    void addTask(Task task) {
        lock_guard<mutex> lock(m_lock); //对当前块的语句加锁， lock_guard 是 mutex 的 stack 封装类，构造的时候 lock()，析构的时候 unlock()
        m_tasks.push(task);
        m_cv.notify_one(); // 唤醒一个线程执行
    }

    // 工作线程主循环函数
    void scheduler() {
        while (!m_isStoped.load()) {
            // 获取一个待执行的 task
            Task task(getTask());

            //while(!m_task_start.load());
            if (task) {
                m_idleThreadNum --;
                task();
                m_idleThreadNum ++;
            }
        }
    }
};

#endif // _THREADING_H
